#include "mempool.h"

thread_local int worker_id = -1;
threadNVMPools the_thread_nvmpools;

static void handleSigbus (int sig) {
    printf("SIGBUS %d is received\n",sig);
    _exit(0);
}

void threadNVMPools::init (int num_workers, const char * nvm_file, long long size) {
    // map_addr must be 4KB aligned, size must be multiple of 4KB
    assert((num_workers>0)&&(size>0)&&(size % 4096 == 0));

    // set sigbus handler
    signal(SIGBUS, handleSigbus);

    // 1. allocate memory
    tm_num_workers = num_workers;
    tm_pools= new mempool[tm_num_workers];
    if (!tm_pools) { perror("malloc"); exit(1); }

    tn_nvm_file= nvm_file;

    //为什么每个线程一个内存池？

    //使每个线程的内存池都是4KB的整数倍
    long long size_per_pool= (size / tm_num_workers / 4096) * 4096;
    size_per_pool = (size_per_pool < MB ? MB : size_per_pool);  //最小的线程空间限定为1MB
    tm_size = size_per_pool * tm_num_workers;

    // pmdk allows PMEM_MMAP_HINT=map_addr to set the map address
    int is_pmem = false;
    size_t mapped_len = tm_size;

    tm_buf = (char *) pmem_map_file(tn_nvm_file, tm_size, PMEM_FILE_CREATE, 0666, &mapped_len, &is_pmem);
    if(tm_buf == NULL) {
        perror("pmem_map_file");
        exit(1);
    }

    printf("NVM mapping address: %p, size: %ld\n", tm_buf, mapped_len);
    if(tm_size != mapped_len) {
        fprintf(stderr, "Error: cannot map %lld bytes\n", tm_size);
        pmem_unmap(tm_buf, mapped_len);
        exit(1);
    }

    // 2. initialize NVM memory pools
    char name[80];
    for(int i=0; i<tm_num_workers; i++) {
        sprintf(name, "NVM pool %d", i);
        // 表示以 4KB大小对齐？
        tm_pools[i].init(tm_buf + i * size_per_pool, size_per_pool, 4096, strdup(name));
    }

    // 3. touch every page to make sure that they are allocated
    for(long long i = 0; i < tm_size; i += 4096) {
        tm_buf[i] = 1;  // XXX: need a special signature
    }
}

threadNVMPools::~threadNVMPools() {
    if(tm_buf) {
        pmem_unmap(tm_buf, tm_size);
        tm_buf= NULL;
    }
    if(tm_pools) {
        delete[] tm_pools;
        tm_pools= NULL;
    }
}

void threadNVMPools::print(void) {
    if(tm_pools==NULL) {
        printf("Error: threadNVMPools is not yet initialized!\n");
        return;
    }

    printf("threadNVMPools\n");
    printf("--------------------\n");
    for(int i=0; i<tm_num_workers; i++) {
        tm_pools[i].print_params();
        tm_pools[i].print_free_nodes();
        printf("--------------------\n");
    }
}

void threadNVMPools::print_usage(void) {
    printf("threadNVMPools\n");
    printf("--------------------\n");
    for (int i=0; i<tm_num_workers; i++) {
        tm_pools[i].print_usage();
    }
    printf("--------------------\n");
}

void * test3ThMain(void *arg) {
    worker_id = (int) (long long) arg;

    // 1. alloc and free
    char * p= (char *)nvmpool_alloc(worker_id * 64);
    printf("alloc(%d)= %p\n", worker_id * 64, p);

    // 3. alloc_node, free_node
    char *pnode[5];
    for (int i=0; i<5; i++) {
        pnode[i] = (char *)nvmpool_alloc_node(64);
        printf("worker %d nvmpool_alloc_node(64)=%p\n", worker_id, pnode[i]);
    }

    printf("worker %d free all allocated nodes\n", worker_id);
    for (int i=0; i<5; i++) {
        nvmpool_free_node(pnode[i]);
    }

    // 4. alloc node again
    for (int i=0; i<3; i++) {
        pnode[i]= (char *)nvmpool_alloc_node(64);
        printf("worker %d nvmpool_alloc_node(64)=%p\n", worker_id, pnode[i]);
    }

    return NULL;
}

void test3() {
    pthread_t tid[3];

    printf("test3\n");

    the_thread_nvmpools.init(3, "leafdata", MB);
    the_thread_nvmpools.print();
    printf("++++++++++++++++++++++++++++++++++++++++++++++\n");
    
    for (int i=0; i<3; i++) {
        if(pthread_create(&(tid[i]), NULL, test3ThMain, (void *)(long long)i) != 0) {
            fprintf(stderr, "pthread_create error!\n"); exit(1);
        }
    }

    for (int i=0; i<3; i++) {
        pthread_join(tid[i], NULL);
    }
    printf("++++++++++++++++++++++++++++++++++++++++++++++\n");
    the_thread_nvmpools.print();
}

int main() {
    test3();
    return 0;
}

//g++ -O3 -std=c++11 -pthread mempool.cpp -lpmem -o dbg-mempool

//0x7f8ac8000000
//0x7f732b000000
//0x7f0ac3200000